package com.gitee.apanlh.util.encode;

import com.gitee.apanlh.util.base.StringUtils;
import com.gitee.apanlh.util.cache.local.CacheFifo;

import java.math.BigInteger;

/**	
 * 	二进制工具类
 * 	
 * 	@author Pan
 */
public class BinaryUtils {
	
	static final int INT_BYTES = 4;
	
	private static final CacheFifo<Long,    String> CACHE_LONG 	= new CacheFifo<>(64);
	private static final CacheFifo<Integer, String> CACHE_INT 	= new CacheFifo<>(64);
	private static final CacheFifo<Short,   String> CACHE_SHORT = new CacheFifo<>(64);
	private static final CacheFifo<Byte,    String> CACHE_BYTE 	= new CacheFifo<>(64);
	
	/**
	 * 	构造函数
	 * 
	 * 	@author Pan
	 */
	private BinaryUtils() {
		//	不允许外部实例
		super();
	}
	
	/**	
	 * 	将long转换成二进制字符串
	 * 	
	 * 	@author Pan
	 * 	@param 	l	long
	 * 	@return	String
	 */
	public static String toStr(long l) {
		String result = CACHE_LONG.get(l);
		if (result == null) {
			return CACHE_LONG.put(l, Long.toBinaryString(l));
		}
		return result;
	}
	
	/**	
	 * 	将Long转换成二进制字符串
	 * 	
	 * 	@author Pan
	 * 	@param 	l	long
	 * 	@return	String
	 */
	public static String toStr(Long l) {
		if (l == null) {
			return null;
		}
		return toStr(l.longValue());
	}
	
	/**	
	 * 	将int转换成二进制字符串
	 * 	
	 * 	@author Pan
	 * 	@param 	i	int
	 * 	@return	String
	 */
	public static String toStr(int i) {
		String result = CACHE_INT.get(i);
		if (result == null) {
			return CACHE_INT.put(i, Integer.toBinaryString(i));
		}
		return result;
	}
	
	/**	
	 * 	将Integer转换成二进制字符串
	 * 	
	 * 	@author Pan
	 * 	@param 	i	int
	 * 	@return	String
	 */
	public static String toStr(Integer i) {
		if (i == null) {
			return null;
		}
		return toStr(i.intValue());
	}
	
	/**	
	 * 	将short转换成二进制字符串
	 * 	
	 * 	@author Pan
	 * 	@param 	s	short
	 * 	@return	String
	 */
	public static String toStr(short s) {
		String result = CACHE_SHORT.get(s);
		if (result == null) {
			return CACHE_SHORT.put(s, Integer.toBinaryString(s & 0xffff));
		}
		return result;
	}
	
	/**	
	 * 	将short转换成二进制字符串
	 * 	
	 * 	@author Pan
	 * 	@param 	s	short
	 * 	@return	String
	 */
	public static String toStr(Short s) {
		if (s == null) {
			return null;
		}
		return toStr(s.shortValue());
	}
	
	/**	
	 * 	将字节转换成二进制字符串
	 * 	
	 * 	@author Pan
	 * 	@param 	b	byte
	 * 	@return	String
	 */
	public static String toStr(byte b) {
		String result = CACHE_BYTE.get(b);
		if (result == null) {
			return CACHE_BYTE.put(b, Integer.toBinaryString(b & 0xff));
		}
		return result;
	}
	
	/**	
	 * 	将字节转换成二进制字符串
	 * 	
	 * 	@author Pan
	 * 	@param 	b	byte
	 * 	@return	String
	 */
	public static String toStr(Byte b) {
		if (b == null) {
			return null;
		}
		return toStr(b.byteValue());
	}
	
	/**	
	 * 	纯数字转换二进制
	 * 	<br>非纯数字报错
	 *  
	 * 	@author Pan
	 * 	@param 	str	String
	 * 	@return	String
	 */
	public static String toStr(String str) {
		if (str == null) {
			return str;
		}
		String binary = new BigInteger(str).toString(2);
		
		//	保证二进制字符串的长度是4的倍数
		int len = binary.length() % INT_BYTES;
		if (len == 0) {
			return binary;
		}
		
		//	从左开始插入补足0
		StringBuilder createBuilder = StringUtils.createBuilder();
		for (int i = INT_BYTES - len; i > 0; i--) {
			createBuilder.append("0");
		}
		return StringUtils.append(binary, createBuilder.toString(), 0);
	}
	
	/**
	 * 	此方法与其他方式转换二进制方式不同
	 * 	<br>此方法是将每个字符转换为其 ASCII 码的二进制表示(每个字符分组成8位进行转换)
	 * 	
	 * 	@author Pan
	 * 	@param 	str	String
	 * 	@return	String
	 */
	public static String toStrByAscii(String str) {
		if (str == null) {
			return str;
		}
		byte[] bytes = str.getBytes();
		StringBuilder createBuilder = StringUtils.createBuilder();
		
		for (int i = 0; i < bytes.length; i++) {
			createBuilder.append(Integer.toBinaryString(bytes[i] & 255 | 256).substring(1));
		}
		return createBuilder.toString();
	}

	/**	
	 * 	将二进制解析成int
	 * 	
	 * 	@author Pan
	 * 	@param 	s	String
	 * 	@return	int
	 */	
	public static int parseToInt(String s) {
		return (int) parseToLong(s);
	}
	
	/**	
	 * 	将二进制解析成Integer
	 * 	
	 * 	@author Pan
	 * 	@param 	s	String
	 * 	@return	Integer
	 */
	public static Integer parseToIntWrapper(String s) {
		return Integer.valueOf(parseToInt(s));
	}
	
	/**	
	 * 	将二进制解析成long
	 * 	
	 * 	@author Pan
	 * 	@param 	s	String
	 * 	@return	long
	 */
	public static long parseToLong(String s) {
		return Long.parseLong(s, 2);
	}
	
	/**	
	 * 	将二进制解析成Long
	 * 	
	 * 	@author Pan
	 * 	@param 	s	String
	 * 	@return	Long
	 */
	public static Long parseToLongWrapper(String s) {
		return Long.valueOf(parseToLong(s));
	}
	
	/**	
	 * 	将二进制解析成short
	 * 	
	 * 	@author Pan
	 * 	@param 	s	String
	 * 	@return	short
	 */
	public static short parseToShort(String s) {
		return (short) parseToLong(s);
	}
	
	/**	
	 * 	将二进制解析成Short
	 * 	
	 * 	@author Pan
	 * 	@param 	s	String
	 * 	@return	Short
	 */
	public static Short parseToShortWrapper(String s) {
		return Short.valueOf(parseToShort(s));
	}
	
	/**	
	 * 	将二进制解析成byte
	 * 	
	 * 	@author Pan
	 * 	@param 	s	String
	 * 	@return	byte
	 */
	public static byte parseToByte(String s) {
		return (byte) parseToLong(s);
	}
	
	/**	
	 * 	将二进制解析成Byte
	 * 	
	 * 	@author Pan
	 * 	@param 	s	String
	 * 	@return	Byte
	 */
	public static Byte parseToByteWrapper(String s) {
		return Byte.valueOf(parseToByte(s));
	}
	
	/**	
	 * 	将二进制解析成Str
	 * 	
	 * 	@author Pan
	 * 	@param 	s	String
	 * 	@return	String
	 */
	public static String parseToStr(String s) {
		if (s == null) {
			return null;
		}
		return new BigInteger(s, 2).toString(10);
	}
	
	/**
	 * 	还原ASCII码的二进制字符串
	 * 	<br>必须是由toStrByAscii此方法进行转换二进制才可解析
	 * 	
	 * 	@author Pan
	 * 	@param 	s	String
	 * 	@return	String
	 */
	public static String parseToStrByAscii(String s) {
		if (s == null) {
			return s;
		}
		
		//	计算分组长度(8位1组)
		int groupMaxLen = 8;
		char[] chars = s.toCharArray();
	    int groupLen = chars.length / groupMaxLen;
	    byte[] bytes = new byte[groupLen];
	    
	    //	还原字符串
	    for (int i = 0; i < groupLen; i++) {
	        int start = i * groupMaxLen;
	        bytes[i] = (byte) ((chars[start] & 1)    << 7 |
	                          (chars[start + 1] & 1) << 6 |
	                          (chars[start + 2] & 1) << 5 |
	                          (chars[start + 3] & 1) << 4 |
	                          (chars[start + 4] & 1) << 3 |
	                          (chars[start + 5] & 1) << 2 |
	                          (chars[start + 6] & 1) << 1 |
	                          (chars[start + 7] & 1));
	    }
	    return StrEncodeUtils.utf8EncodeToStr(bytes);
	}
	
}
